/*
 *
 * File Name: dbus_test_comm_loop.c
 * Author: liaoxuecheng
 * mail: liaoxuecheng@hotmail.com
 * Created Time: 2025年01月16日 星期四 14时43分24秒
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dbus/dbus.h>
#include <sys/epoll.h>

#include "at_dbus.h"
#include "tests.h"
#include "ev.h"
#include "event.h"

#define THIS_FILE "dbus_test_comm_loop.c"

#ifndef PJ_LOG
#define TEST_PRINTF(title,fmt,arg...) printf("%s:%d %s, "fmt, __func__, __LINE__, title, ##arg)
#define PJ_LOG(arg, fmt) { printf("level = %d, ",arg);TEST_PRINTF fmt; }
#endif

static int (*test_loop_func)(DBusConnection *) = NULL;

struct DBus_ev_loop
{
    DBusConnection *conn;
    struct ev_loop *loop;
};

static void inline read_write_dispatch(DBusConnection * conn)
{
    do
    {
        dbus_connection_read_write_dispatch(conn, 0);
    }while(dbus_connection_get_dispatch_status(conn) == DBUS_DISPATCH_DATA_REMAINS);
}

void dbus_io_cb(struct ev_loop *loop, ev_io *w, int revents) {
    DBusConnection *conn = (DBusConnection *)w->data;
    read_write_dispatch(conn);
}

void dbus_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
    DBusConnection *conn = (DBusConnection *)w->data;
    read_write_dispatch(conn);
}

dbus_bool_t dbus_watch_add_function(DBusWatch *watch, void *data)
{
    static ev_io dbus_loop_watcher;
    struct DBus_ev_loop *d = (struct DBus_ev_loop *)data;
    const int fd = dbus_watch_get_unix_fd(watch);

    if(dbus_watch_get_enabled(watch))
    {
        PJ_LOG(1, (THIS_FILE, "\e[33;1m%s fd:%d enabled!\e[0m\n", __func__, fd));
        return TRUE;
    }

    ev_io *dbus_watcher = &dbus_loop_watcher;
    ev_io_init(dbus_watcher, dbus_io_cb, fd, EV_READ);
    dbus_watcher->data = d->conn;
    ev_io_start(d->loop, dbus_watcher);
    PJ_LOG(1, (THIS_FILE, "\e[33;1m%s fd:%d\e[0m\n", __func__, fd));
    return TRUE;
}

static int simple_ev1_loop(DBusConnection *conn)
{
    struct DBus_ev_loop data;
    struct ev_loop *loop = ev_default_loop(0); // 获取默认的 libev 事件循环
    data.conn = conn;
    data.loop = loop;

    PJ_LOG(1, (THIS_FILE, "\e[33;1m%s\e[0m\n", __func__));
    dbus_connection_set_watch_functions(conn,
            dbus_watch_add_function,
            NULL,
            NULL,
            (void*)&data, NULL);

    ev_timer dbus_timeout_watcher;
    ev_timer_init(&dbus_timeout_watcher, dbus_timeout_cb, 0.0, 0.5);
    dbus_timeout_watcher.data = conn;
    ev_timer_start(loop, &dbus_timeout_watcher);

    ev_run(loop, 0);
    ev_loop_destroy(loop);
    return 0;
}

static int simple_loop_self(DBusConnection *conn)
{
    PJ_LOG(1, (THIS_FILE, "\e[33;1m%s\e[0m\n", __func__));
    return 0;
}

static int simple_ev_loop(DBusConnection *conn)
{
    int fd = 0;
    struct ev_loop *loop = ev_default_loop(0);
    dbus_connection_get_unix_fd(conn, &fd);
    PJ_LOG(1, (THIS_FILE, "\e[33;1m%s fd: %d\e[0m\n", __func__, fd));

    ev_io dbus_loop_watcher;
    ev_io_init(&dbus_loop_watcher, dbus_io_cb, fd, EV_READ);
    dbus_loop_watcher.data = conn;
    ev_io_start(loop, &dbus_loop_watcher);
         
    ev_timer dbus_timeout_watcher;
    ev_timer_init(&dbus_timeout_watcher, dbus_timeout_cb, 0.0, 0.5);
    dbus_timeout_watcher.data = conn;
    ev_timer_start(loop, &dbus_timeout_watcher);

    ev_run(loop, 0);
    ev_loop_destroy(loop);
    return 0;
}

static int simple_select_loop(DBusConnection *conn)
{
    fd_set rfds;
    int conn_fd = 0;
    dbus_connection_get_unix_fd(conn, &conn_fd);
    PJ_LOG(1, (THIS_FILE, "\e[33;1m%s fd: %d\e[0m\n", __func__, conn_fd));
    struct timeval tv;
    while (1)
    {
        FD_ZERO(&rfds);
        FD_SET(conn_fd, &rfds);
        tv.tv_sec = 0;
        tv.tv_usec = 500000;

        int ret = select(conn_fd + 1, &rfds, NULL, NULL, &tv);
        if(!FD_ISSET(conn_fd, &rfds))
        {
            continue;
        }
        if (ret > 0)
        {
            read_write_dispatch(conn);
        }
        else if(ret == 0)
        {
            read_write_dispatch(conn);
        }
        else
        {
            PJ_LOG(1, (THIS_FILE, "\e[33;1m%s err:%d %m!\e[0m\n", __func__, conn_fd));
            usleep(100);
        }
    }
    return 0;
}

static int simple_epoll_loop(DBusConnection *conn)
{
#define MAX_EVENTS 5
    int num_fds, conn_fd = 0;
    struct epoll_event event;
    struct epoll_event events[MAX_EVENTS];
    int epfd = epoll_create1(EPOLL_CLOEXEC);
    if(epfd == -1)
    {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    dbus_connection_get_unix_fd(conn, &conn_fd);
    PJ_LOG(1, (THIS_FILE, "\e[33;1m%s fd: %d\e[0m\n", __func__, conn_fd));

    // 添加监听套接字到epoll实例
    event.data.fd = conn_fd;
    event.events = EPOLLIN | EPOLLET;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &event) == -1)
    {
        perror("epoll_ctl: listen_sock");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        num_fds = epoll_wait(epfd, events, MAX_EVENTS, 500 /* milliseconds */);
        if(num_fds == -1)
        {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }
        else if(num_fds == 0)
        {
            read_write_dispatch(conn);
        }

        for(int i = 0; i < num_fds; i++)
        {
            if (events[i].data.fd == conn_fd)
            {
                read_write_dispatch(conn);
            }
        }
	}
    close(epfd);
    return 0;
}

int simple_loop(DBusConnection *conn)
{
    if(test_loop_func)
        return test_loop_func(conn);
    return simple_select_loop(conn);
}

int init_args(int argc, char **argv)
{
    if(argc == 1)
        return 0;
    PJ_LOG(1, (THIS_FILE, "\e[33;1m%s [1/2/3/4]!\e[0m\n", argv[0]));
    int i = atoi(argv[1]);
    if(i == 1)
        test_loop_func = simple_epoll_loop;
    else if(i == 2)
        test_loop_func = simple_ev_loop;
    else if(i == 3)
        test_loop_func = simple_ev1_loop;
    else if(i == 4)
        test_loop_func = simple_loop_self;
    else
        test_loop_func = simple_select_loop;
    return 0;
}
